import fs from 'node:fs/promises'
import path from 'node:path'
import os from 'node:os'
import { fileURLToPath } from 'node:url'
import chalk from 'chalk'
import ora from 'ora'
import inquirer from 'inquirer'
import { simpleGit } from 'simple-git'
import validatePackageName from 'validate-npm-package-name'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// 远程模板仓库地址
const TEMPLATE_REPO_URL = 'https://gitee.com/tengteng_fan/hzy-uni-template.git'

/**
 * 项目配置接口
 */
interface ProjectConfig {
    projectName: string
    description: string
    author: string
}

/**
 * 验证项目名称
 * @param name 项目名称
 * @returns 验证结果
 */
function validateProjectName(name: string): {
    valid: boolean
    problems?: string[]
} {
    const result = validatePackageName(name)

    if (!result.validForNewPackages) {
        return {
            valid: false,
            problems: [...(result.errors || []), ...(result.warnings || [])],
        }
    }

    return { valid: true }
}

/**
 * 生成临时目录路径
 * @returns 临时目录路径
 */
function getTempDir(): string {
    return path.join(os.tmpdir(), `hzy-uni-template-${Date.now()}`)
}

/**
 * 从远程仓库克隆模板
 * @param tempDir 临时目录路径
 * @returns Promise<void>
 */
async function cloneTemplate(tempDir: string): Promise<void> {
    const spinner = ora('正在从远程仓库获取最新模板...').start()

    try {
        const git = simpleGit()
        await git.clone(TEMPLATE_REPO_URL, tempDir, ['--depth', '1'])
        spinner.succeed('模板获取成功')
    } catch (error) {
        spinner.fail('模板获取失败')
        throw new Error(`无法克隆模板仓库: ${error}`)
    }
}

/**
 * 复制模板文件到目标目录
 * @param templatePath 模板路径
 * @param targetPath 目标路径
 * @returns Promise<void>
 */
async function copyTemplate(
    templatePath: string,
    targetPath: string
): Promise<void> {
    const spinner = ora('正在复制模板文件...').start()

    // 排除的文件和目录
    const excludePatterns = [
        'node_modules',
        'dist',
        '.git',
        'pnpm-lock.yaml',
        'package-lock.json',
        '.DS_Store',
        'hzy-uni-cli', // 排除插件目录本身
    ]

    /**
     * 递归复制目录
     * @param src 源目录
     * @param dest 目标目录
     */
    async function copyDir(src: string, dest: string): Promise<void> {
        await fs.mkdir(dest, { recursive: true })
        const entries = await fs.readdir(src, { withFileTypes: true })

        for (const entry of entries) {
            if (excludePatterns.includes(entry.name)) continue

            const srcPath = path.join(src, entry.name)
            const destPath = path.join(dest, entry.name)

            if (entry.isDirectory()) {
                await copyDir(srcPath, destPath)
            } else {
                await fs.copyFile(srcPath, destPath)
            }
        }
    }

    try {
        await copyDir(templatePath, targetPath)
        spinner.succeed('模板文件复制完成')
    } catch (error) {
        spinner.fail('模板文件复制失败')
        throw error
    }
}

/**
 * 更新项目配置文件
 * @param projectPath 项目路径
 * @param config 项目配置
 * @returns Promise<void>
 */
async function updateProjectConfig(
    projectPath: string,
    config: ProjectConfig
): Promise<void> {
    const spinner = ora('正在更新项目配置...').start()

    try {
        // 更新 package.json
        const packageJsonPath = path.join(projectPath, 'package.json')
        const packageJson = JSON.parse(
            await fs.readFile(packageJsonPath, 'utf-8')
        )
        packageJson.name = config.projectName
        packageJson.description = config.description
        packageJson.author = config.author
        await fs.writeFile(
            packageJsonPath,
            JSON.stringify(packageJson, null, 2)
        )

        // 更新 env/.env 文件
        const envPath = path.join(projectPath, 'env/.env')
        try {
            let envContent = await fs.readFile(envPath, 'utf-8')
            envContent = envContent.replace(
                /VITE_APP_TITLE\s*=\s*'.*'/,
                `VITE_APP_TITLE = '${config.projectName}'`
            )
            await fs.writeFile(envPath, envContent)
        } catch {
            // 如果 env/.env 文件不存在，跳过更新
        }

        // 更新 manifest.config.ts 文件
        const manifestPath = path.join(projectPath, 'manifest.config.ts')
        try {
            let manifestContent = await fs.readFile(manifestPath, 'utf-8')
            manifestContent = manifestContent.replace(
                /name: '.*'/,
                `name: '${config.projectName}'`
            )
            await fs.writeFile(manifestPath, manifestContent)
        } catch {
            // 如果 manifest.config.ts 文件不存在，跳过更新
        }

        spinner.succeed('项目配置更新完成')
    } catch (error) {
        spinner.fail('项目配置更新失败')
        throw error
    }
}

/**
 * 清理临时文件
 * @param tempDir 临时目录路径
 * @returns Promise<void>
 */
async function cleanupTemp(tempDir: string): Promise<void> {
    try {
        await fs.rm(tempDir, { recursive: true, force: true })
    } catch {
        // 忽略清理错误
    }
}

/**
 * 创建新项目
 * @param projectInput 项目名称或路径
 * @returns Promise<void>
 */
async function createProject(projectInput: string): Promise<void> {
    // 解析项目路径和名称
    const projectPath = path.resolve(projectInput)
    const projectName = path.basename(projectPath)
    console.log(chalk.blue(`\n🚀 正在创建项目: ${projectName}\n`))

    // 验证项目名称
    const validation = validateProjectName(projectName)
    if (!validation.valid) {
        console.log(chalk.red('❌ 项目名称无效:'))
        validation.problems?.forEach((problem) => {
            console.log(chalk.red(`  - ${problem}`))
        })
        return
    }

    const targetPath = projectPath
    try {
        await fs.access(targetPath)
        console.log(chalk.red(`错误: 目录 ${projectName} 已存在`))
        return
    } catch {
        // 目录不存在，可以继续
    }

    let tempDir: string | null = null

    try {
        // 生成临时目录
        tempDir = getTempDir()

        // 从远程仓库克隆模板
        await cloneTemplate(tempDir)

        // 复制模板文件
        await copyTemplate(tempDir, targetPath)

        // 生成项目配置（简化版，只需要基本信息）
        const config: ProjectConfig = {
            projectName,
            description: `基于 hzy-uni-template 创建的 ${projectName} 项目`,
            author: 'Your Name',
        }

        // 更新项目配置
        await updateProjectConfig(targetPath, config)

        console.log(chalk.green(`\n✅ 项目创建成功!\n`))
        console.log(chalk.cyan('下一步:'))
        console.log(chalk.white(`  cd ${projectName}`))
        console.log(chalk.white('  pnpm install'))
        console.log(chalk.white('  pnpm dev:h5'))
        console.log()
    } catch (error) {
        console.error(chalk.red('创建项目失败:'), error)

        // 如果创建失败，尝试清理已创建的目录
        try {
            await fs.rm(targetPath, { recursive: true, force: true })
        } catch {
            // 忽略清理错误
        }
    } finally {
        // 清理临时文件
        if (tempDir) {
            await cleanupTemp(tempDir)
        }
    }
}

/**
 * 主函数
 * @returns Promise<void>
 */
/**
 * 主函数 - 处理命令行参数和用户交互
 * @returns Promise<void>
 */
export async function main(): Promise<void> {
    const args = process.argv.slice(2)
    let projectName: string

    if (args.length === 0) {
        // 没有提供项目名称，引导用户输入
        console.log(chalk.cyan('🚀 欢迎使用 hzy-uni-cli 项目脚手架工具!'))
        console.log()

        const answers = await inquirer.prompt([
            {
                type: 'input',
                name: 'projectName',
                message: '请输入项目名称:',
                default: 'uni-project',
                validate: (input: string) => {
                    const name = input.trim() || 'uni-project'
                    const validation = validateProjectName(name)
                    if (!validation.valid) {
                        return validation.problems?.[0] || '项目名称无效'
                    }
                    return true
                },
                filter: (input: string) => input.trim() || 'uni-project',
            },
        ])

        projectName = answers.projectName
    } else {
        // 使用命令行参数提供的项目名称
        projectName = args[0]
    }

    await createProject(projectName)
}

// 如果直接运行此文件
if (import.meta.url === `file://${process.argv[1]}`) {
    main().catch(console.error)
}
